/*
 * JoinComponentDefinition.java
 *
 * Created on September 27, 2006, 11:37 PM
 *
 * (C) 2006 R. Alexander Milowski
 */

package com.smallx.xproc.library;

import com.smallx.xproc.ComponentType;
import java.net.URI;
import java.util.Collection;
import org.infoset.component.Component;
import org.infoset.component.ComponentBase;
import org.infoset.component.ComponentDefinition;
import org.infoset.xml.Document;
import org.infoset.xml.Item;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentDestination;
import org.infoset.xml.util.DocumentSource;

/**
 *
 * @author alex
 */
public class GateComponentDefinition implements ComponentDefinition {

   static ComponentType defaultType = new ComponentType(LibraryComponents.GATE_TYPE);
   static Name INPUT_PORT = Name.create("input");
   static Name OUTPUT_PORT = Name.create("output");
   static {
      defaultType.addInputPort(INPUT_PORT,true);
      defaultType.addOutputPort(OUTPUT_PORT,true);
   }
   protected ComponentType type;
   
   static class GateComponent extends ComponentBase implements IterationListener {
      ItemDestination destination;
      Document cachedDocument = null;
      int notifies = 0;
      GateComponent() {
         super(LibraryComponents.GATE_TYPE);
         destination = null;
         notifies = 0;
         cachedDocument = null;
      }
      
      public void bindOutputPort(Name port,ItemDestination dest) 
         throws XMLException
      {
         if (!port.equals(OUTPUT_PORT)) {
            throw new XMLException("There is no output port named "+port+" on the pipeline "+LibraryComponents.GATE_TYPE);
         }
         destination = dest;
      }

      public void onStart()
         throws XMLException
      {
         cachedDocument = null;
      }
         
      protected void openGate() 
         throws XMLException
      {
         if (notifies>0 && cachedDocument!=null) {
            notifies--;
            DocumentSource.generate(cachedDocument,false,destination);
         }
      }

      public ItemDestination getInputPort(Name port) 
         throws XMLException
      {
         if (!port.equals(INPUT_PORT)) {
            throw new XMLException("There is no input port named "+port+" on the pipeline "+LibraryComponents.GATE_TYPE);
         }
         return new DocumentDestination() {
            public void send(Item item)
               throws XMLException
            {
               super.send(item);
               if (item.getType()==Item.ItemType.DocumentEndItem) {
                  cachedDocument = getDocument();
                  openGate();
               }
            }
         };
      }

      public void onDocumentInput(Name port,Document doc) 
         throws XMLException
      {
         if (!port.equals(INPUT_PORT)) {
            throw new XMLException("There is no input port named "+port+" on the pipeline "+LibraryComponents.GATE_TYPE);
         }
         cachedDocument = doc;
      }
      
      public void notifyStart() 
         throws XMLException
      {
         context.getLog().fine("Gate notified of iteration");
         notifies++;
         openGate();
      }
      
      public void notifyEnd() 
         throws XMLException
      {
      }

      public void onEnd()
         throws XMLException
      {
         cachedDocument = null;
      }

   }
   
   public GateComponentDefinition() {
      this(defaultType);
   }
   /** Creates a new instance of FlowComponentDefinition */
   public GateComponentDefinition(ComponentType type) {
      this.type = type;
   }
   
   public Component newInstance()
      throws XMLException 
   {
      return new GateComponent();
   }
      
   public Name getName() {
      return type.getName();
   }

   public String getVersion() {
      return null;
   }

   public String getVendor() {
      return null;
   }

   public URI getVendorLocation() {
      return null;
   }

   public Collection<Name> getInputs() {
      return type.getInputPortNames();
   }

   public Collection<Name> getOutputs() {
      return type.getOutputPortNames();
   }

}

